from typing import Dict, List, Optional, Any

from .edge import Edge
from ..nodes.base_node import BaseNode
from ..nodes.start_node import StartNode
from ..nodes.node_factory import NodeFactory


class FlowGraph:
    """
    流程图模型类（单一职责：负责承载流程定义并提供图结构的查询能力）。
    
    设计原则：
    - 一个文件一个类，面向对象、单一职责
    - 方法入参与出参具备明确的类型与注释
    - 属性具有类型注解与中文说明，提升可读性与可维护性
    
    Attributes:
        flow (Dict[str, Any]): 原始流程定义（JSON 对象），包含 nodes 与 edges 等字段
        nodes_by_id (Dict[str, BaseNode]): 根据节点 ID 索引的节点对象字典，便于快速访问
        edges_by_source (Dict[str, List[Edge]]): 根据边的来源节点 ID 分组的边列表映射，用于路由计算
    """
    # 面向对象属性类型注解与说明
    flow: Dict[str, Any]
    nodes_by_id: Dict[str, BaseNode]
    edges_by_source: Dict[str, List[Edge]]

    def __init__(self, flow: Dict[str, Any]) -> None:
        """
        初始化流程图对象。
        
        Args:
            flow (Dict[str, Any]): 完整的流程配置字典（例如包含 'nodes' 与 'edges' 字段的 JSON 对象）
        """
        # 保存原始流程配置
        self.flow = flow
        # 构建按 ID 索引的节点表
        self.nodes_by_id = {}
        for nd in flow.get('nodes', []) or []:
            node_obj = NodeFactory.create(nd)
            self.nodes_by_id[node_obj.id] = node_obj
        # 构建来源节点 -> 边列表的映射
        self.edges_by_source = {}
        for e in flow.get('edges', []) or []:
            edge_obj = Edge.from_dict(e)
            self.edges_by_source.setdefault(edge_obj.source, []).append(edge_obj)

    def get_outgoing_edges(self, node_id: str) -> List[Edge]:
        """
        根据节点 ID 获取其所有出边（从该节点出发的边）。
        
        Args:
            node_id (str): 节点唯一标识符
        
        Returns:
            List[Edge]: 从该节点出发的边对象列表；若不存在则返回空列表
        """
        return self.edges_by_source.get(node_id, [])

    def find_start_node(self) -> Optional[BaseNode]:
        """
        查找并返回流程的起始节点。
        
        规则：
        - 优先匹配节点类型是 StartNode 的实例
        - 或者节点的类型字段等于 'start'
        
        Returns:
            Optional[BaseNode]: 起始节点对象；如果未找到返回 None
        """
        for n in self.nodes_by_id.values():
            if isinstance(n, StartNode) or n.type == 'start':
                return n
        return None

    def validate(self) -> None:
        """
        校验流程图结构的基本一致性，包括：
        - 至少存在一个有效的起始节点
        - 所有节点必须具有非空的 id
        - 所有边的 source/target 必须为非空且对应的节点存在
        - 节点 id 不允许重复（忽略空 id）
        """
        # 检查起始节点
        if self.find_start_node() is None:
            raise ValueError("Start node not found")
        # 检查节点 id 非空与重复
        raw_nodes = self.flow.get('nodes', []) or []
        ids = [str(n.get('id') or '').strip() for n in raw_nodes]
        non_empty_ids = [i for i in ids if i]
        if len(set(non_empty_ids)) != len(non_empty_ids):
            raise ValueError("Duplicate node ids")
        for node_id in self.nodes_by_id.keys():
            if not node_id or node_id.strip() == "":
                raise ValueError("Node id is required")
        # 检查边合法性
        raw_edges = self.flow.get('edges', []) or []
        for e in raw_edges:
            source = str(e.get('from') or e.get('source') or '').strip()
            target = str(e.get('to') or e.get('target') or '').strip()
            if not source or not target:
                raise ValueError("Edge source/target must not be empty")
            if source not in self.nodes_by_id:
                raise ValueError(f"Edge source '{source}' not found in nodes")
            if target not in self.nodes_by_id:
                raise ValueError(f"Edge target '{target}' not found in nodes")